<?php


namespace App\Services;


use App\Constants\PayTypeConstant;
use App\Constants\RefundStatusConstant;
use App\Constants\VipOrderStatusConstant;
use App\Exceptions\BusinessException;
use App\Models\MemberVipOrder;
use App\Models\PayLog;
use App\Models\RefundLog;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class RefundService
{
    public function refund(int $orderId): array
    {
        $order = MemberVipOrder::find($orderId);

        if (!$order) {
            return resultFailedReturn("订单不存在");
        }

        if ($order->status != VipOrderStatusConstant::IS_PAY) {
            return resultFailedReturn("该订单无法退款");
        }

        $totalRefundAmount = RefundLog::where([
            'type' => PayTypeConstant::TYPE_BY_BUY_VIP, 'order_id' => $orderId,
        ])->where('refund_status', '<>', RefundStatusConstant::REFUND_FAILED)->sum('refund_amount') ?: 0;

        if ($totalRefundAmount >= $order->pay_amount) {
            return resultFailedReturn("该订单已全部退款");
        }

        $pay = PayLog::where(['type' => PayTypeConstant::TYPE_BY_BUY_VIP, 'pay_sn' => $order->pay_sn])->first();
        if (!$pay) {
            return resultFailedReturn('支付记录缺失');
        }

        $orderAmount = $order->pay_amount;
        $refundAmount = bcsub($orderAmount, $totalRefundAmount);

        $refundNo = generateUniqueSn();

        RefundLog::create([
            'mini_type'       => $order->mini_type,
            'type'            => $pay->type,
            'order_id'        => $orderId,
            'order_sn'        => $order->order_sn,
            'pay_sn'          => $order->pay_sn,
            'pay_amount'      => $orderAmount,
            'pay_type'        => $pay->pay_type,
            'pay_trade_no'    => $pay->trade_no,
            'refund_no'       => $refundNo,
            'refund_amount'   => $refundAmount,
            'refund_status'   => RefundStatusConstant::REFUND_ING,
            'failed_msg'      => '',
            'refund_trade_no' => '',
        ]);

        $config = json_decode($pay->extend, true);

        if (isset($config['notify_url'])) {
            unset($config['notify_url']);
        }

        try {
            $wechatPaymentService = new WechatPaymentService($config);
            $ret = $wechatPaymentService->refundByTradeNo($pay->trade_no, $refundNo, $orderAmount, $refundAmount, [
                'notify_url' => ngrokUrl('wechat_notify.vip_refund', ['sn' => $refundNo]),
            ]);

            if ($ret['return_code'] !== 'SUCCESS') {
                $this->updateRefundLog($refundNo, [
                    'refund_status' => RefundStatusConstant::REFUND_FAILED,
                    'failed_msg'    => $ret['return_msg'] ?? '退款失败'
                ]);
                return resultFailedReturn($ret['return_msg'] ?? '退款失败');
            }

            if ($ret['result_code'] !== 'SUCCESS') {
                $this->updateRefundLog($refundNo, [
                    'refund_status' => RefundStatusConstant::REFUND_FAILED,
                    'failed_msg'    => $ret['err_code_des'] ?? '退款失败'
                ]);
                return resultFailedReturn($ret['return_msg'] ?? '退款失败');
            }
        } catch (\Throwable $e) {
            log_exception('微信退款异常', $e);
            $this->updateRefundLog($refundNo, [
                'refund_status' => RefundStatusConstant::REFUND_FAILED,
                'failed_msg'    => $e->getMessage()
            ]);
            return resultFailedReturn('微信退款失败:' . $e->getMessage());
        }

        return resultSuccessReturn('订单退款申请成功');
    }

    public function refundNotify(string $refundNo)
    {
        $refund = RefundLog::where('refund_no', $refundNo)->first();
        if (!$refund) {
            return response(<<<XML
<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>
XML
                , 200, ['Content-Type' => 'text/xml']);
        }

        $extend = PayLog::where('pay_sn', $refund->pay_sn)->value('extend');

        $config = json_decode($extend, true);

        if (isset($config['notify_url'])) {
            unset($config['notify_url']);
        }

        $wechatPaymentService = new WechatPaymentService($config);

        return $wechatPaymentService->refundNotify($refundNo, function ($refundNo, $tradeNo) {
            return $this->refundHandel($refundNo, $tradeNo);
        });
    }

    public function refundHandel(string $refundSn, string $tradeNo): bool
    {
        try {
            DB::beginTransaction();

            $log = RefundLog::where('refund_no', $refundSn)->first();
            if (!$log) {

                throw new BusinessException(sprintf('退款单号【%s】 退款记录不存在', $refundSn));
            }
            if ($log->status != RefundStatusConstant::REFUND_ING) {
                throw new BusinessException(sprintf('退款单号【%s】 退款状态错误', $refundSn));
            }

            $log->update([
                'refund_status'   => RefundStatusConstant::REFUND_SUCCESS,
                'refund_trade_no' => $tradeNo,
            ]);

            MemberVipOrder::where('id', $log->order_id)->update([
                'status' => VipOrderStatusConstant::IS_REFUND
            ]);


            DB::commit();
        } catch (BusinessException $bs) {
            Log::error($bs->getMessage());
            DB::rollBack();
            return false;
        } catch (\Throwable $e) {
            DB::rollBack();
            log_exception(sprintf('退款单号【%s】 退款成功 修改状态异常', $refundSn), $e);
            return false;
        }

        return true;
    }

    private function updateRefundLog(string $refundNo, array $data)
    {
        RefundLog::where('refund_no', $refundNo)->update($data);
    }
}
